﻿// News Framework
// (C) Tanzim Saqib. Oct 25, 2010.
// http://TanzimSaqib.com | http://newsfx.codeplex.com

namespace TanzimSaqib.NewsFramework
{
    using System.Linq;
    using System.Xml.Linq;
    using System;
    using System.IO;
    using System.Net;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using System.Text;
    using ICSharpCode.SharpZipLib.GZip;
   
    public sealed class RssItem
    {
        public string Description { get; set; }
        public string Link { get; set; }
        public string Title { get; set; }
        public string ThumbnailURL { get; set; }
        public string Content { get; set; }
    }

    internal sealed class RssResponse
    {
        public List<RssItem> Items { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string URL { get; set; }
        public Exception Error { get; set; }
    }

    internal sealed class RssResponseCallback
    {
        public string URL { get; set; }
        public HttpWebRequest Request { get; set; }
        public int Count { get; set; }
        public Action<RssResponse> Callback { get; set; }
        public IRssItemParser ExtendedParser { get; set; }
    }

    public class RssExtendedParser : IRssItemParser
    {
        public Func<XElement, string> ThumbnailParser { get; set; }
        public Func<XElement, string> ContentParser { get; set; }
    }

    internal sealed class RssClient
    {
        private static readonly Regex _StripTagEx = new Regex("</?[^>]+>", RegexOptions.Compiled);

        private static string StripTags(string html, int trimAt)
        {
            string plainText = _StripTagEx.Replace(html, string.Empty);
            return plainText.Substring(0, Math.Min(plainText.Length, trimAt));
        }

        private static string StripTags(string html)
        {
            return _StripTagEx.Replace(html, string.Empty);
        }

        public static void FetchRssCallback(IAsyncResult result)
        {
            Exception error = null;
            List<RssItem> items = null;
            var title = string.Empty;
            var description = string.Empty;

            RssResponseCallback state = null;
            WebResponse response;

            try
            {
                state = result.AsyncState as RssResponseCallback;
                response = state.Request.EndGetResponse(result);
                string xmlContent = WebUtil.GetResponseText(response);
                //using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")))
                //{

                //string xmlContent = reader.ReadToEnd();
                //var wynik = SanitizeXmlString(xmlContent);
                var feed = XElement.Parse(xmlContent);

                if (feed == null || state.Count < 1)
                {
                    state.Callback(null);
                    return;
                }

                // RSS
                if (feed.Element("channel") != null)
                {
                    items = (from item in feed.Element("channel").Elements("item")
                             select new RssItem
                             {
                                 Title = StripTags(item.Element("title").Value, 200),
                                 Link = item.Element("link").Value,
                                 Description = item.Element("description") == null ? string.Empty : StripTags(item.Element("description").Value),
                                 ThumbnailURL = state.ExtendedParser.ThumbnailParser(item),
                                 Content = state.ExtendedParser.ContentParser(item)
                             }).Take(state.Count).ToList();

                    title = feed.Element("channel").Element("title").Value;
                    description = feed.Element("channel").Element("description").Value;
                }
                else if (feed.Elements("{http://www.w3.org/2005/Atom}entry") != null)
                {
                    items = (from item in feed.Elements("{http://www.w3.org/2005/Atom}entry")
                             select new RssItem
                             {
                                 Title = StripTags(item.Element("{http://www.w3.org/2005/Atom}title").Value, 200),
                                 Link = item.Element("{http://www.w3.org/2005/Atom}id").Value,
                                 Description = item.Element("{http://www.w3.org/2005/Atom}summary") == null ? string.Empty : item.Element("{http://www.w3.org/2005/Atom}summary").Value,
                                 ThumbnailURL = state.ExtendedParser.ThumbnailParser(item),
                                 Content = item.Element("{http://www.w3.org/2005/Atom}content") == null ? string.Empty : item.Element("{http://www.w3.org/2005/Atom}content").Value,
                             }).Take(state.Count).ToList();

                    title = feed.Element("{http://www.w3.org/2005/Atom}title").Value;
                    //description = feed.Element("feed ").Element("description").Value;
                }
                //}
            }
            catch (Exception e)
            {
                error = e;
            }
            finally
            {
                state.Callback(new RssResponse() { URL = state.URL, Items = items, Description = description, Title = title, Error = error });
            }
            
        }

         /// <summary>  
         /// Remove illegal XML characters from a string.  
         /// </summary>  
         private static string SanitizeXmlString(string wynik)  
         {  
   
             StringBuilder buffer = new StringBuilder(wynik.Length);  
   
             foreach (char c in wynik)  
             {  
                 if (IsLegalXmlChar(c))  
                 {  
                     buffer.Append(c);  
                 }  
             }  
   
           return buffer.ToString();  
         }  
   
         private static bool IsLegalXmlChar(int character)  
         {  
             return  
             (  
                  character == 0x9 /* == '\t' == 9   */          ||  
                      character == 0xA /* == '\n' == 10  */          ||  
                      character == 0xD /* == '\r' == 13  */          ||  
                     (character >= 0x20 && character <= 0xD7FF) ||  
                     (character >= 0xE000 && character <= 0xFFFD) ||  
                     (character >= 0x10000 && character <= 0x10FFFF)  
   
             );  
         }  


        internal static void FetchRssAsync(string url, int count, IRssItemParser extendedParser, Action<RssResponse> callback)
        {
            var request = WebRequest.Create(url) as HttpWebRequest;
            request.Method = "GET";

            request.BeginGetResponse(FetchRssCallback, new RssResponseCallback() { URL = url, Request = request, Count = count, ExtendedParser = extendedParser, Callback = callback });  
        }
    }
}
